<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="description" content="ffmpeg命令行使用指南|ffmpeg|ffplay|ffprobe">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ffmpeg命令行使用指南</title>
    <link rel="stylesheet" href="./lib/tw.css">
    <script src="./lib/vue.js"></script>
    <script src="./lib/axios.js"></script>
</head>

<body>
    <div class="w-fulle px-3 mt-2" id="app">
        <div class="flex flex-row mb-1">
            <div class=" px-2 bg-purple-200">命令行工具</div>
            <div class=" px-1 border-t border-b border-r" :class="{'bg-red-200':conType == 'ffprobe'}"
                @click="setConType('ffprobe')">ffprobe</div>
            <div class=" px-1 border-t border-b border-r" :class="{'bg-red-200':conType == 'ffmpeg'}"
                @click="setConType('ffmpeg')">ffmpeg</div>
            <div class=" px-1 border-t border-b border-r" :class="{'bg-red-200':conType == 'ffplay'}"
                @click="setConType('ffplay')">ffplay</div>

            <div class=" px-2 bg-purple-200 ml-5">命令行详情</div>
            <div class=" px-3 border-t border-b border-r" :class="{'bg-red-200':type == 'tutorial'}" @click="type = 'tutorial'">
                教程</div>
            <div class=" px-3 border-t border-b border-r" :class="{'bg-red-200':type == 'help'}" @click="type = 'help'">
                使用方法</div>
            <div class=" px-3 border-t border-b border-r" :class="{'bg-red-200':type == 'options'}"
                @click="type = 'options'">主要选项</div>
            <div class=" px-3 border-t border-b border-r" :class="{'bg-red-200':type == 'detail'}"
                @click="type = 'detail'">选项参数</div>
            <div class="px-3">
                <input type="text" class="border-2" placeholder="请输入选项" v-model="keyword" @change="search" />
            </div>

            <div class=" px-2 bg-purple-200 ml-5">参数大全</div>
            <div class=" px-1 text-indigo-500 underline"><a href="./data/ffmpeg_h_full.txt">ffmpeg</a></div>
            <div class=" px-1 text-indigo-500 underline"> <a href="./data//ffplay_h_full.txt">ffplay</a></div>
            <div class="px-1 text-indigo-500 underline"> <a href="./data/ffprobe_h_full.txt">ffprobe</a></div>

        </div>
        <div class="mt-2">

            <div class=" flex flex-row">
                <div class=" px-1 text-indigo-500">使用方法</div>
                <div class=" px-1 ">{{help.usage}}</div>
            </div>

            <div class="mt-2 tutorial-con" v-if="type == 'tutorial'">
                教程内容
            </div>
            <div class="mt-2 help-con" v-if="keyword || type == 'help'">

                <div class="flex flex-col h-20 w-full mt-5  " v-if="conType == 'ffmpeg'">
                    <div class="flex flex-row justify-center">
                        <span class="p-2 bg-yellow-200">黄色:数据实体</span>
                        <span class="p-2 bg-green-200">绿色:处理过程</span>
                        <span class="p-2 bg-blue-200">说明(点击黄,绿方块查看选项参数)</span>
                    </div>
                    <div class="flex flex-row justify-center mt-5">
                        <div class="p-2 ml-1 w-40  h-full bg-yellow-200 text-center" @click="Show('source')">
                            <span>Source</span><br>
                            <span>数据来源</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-green-200 text-center" @click="Show('protocols-read')">
                            <span>protocols-read</span><br>
                            <span>读取数据</span>
                            <br><span>---&gt;</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-yellow-200 text-center" @click="Show('Input')">
                            <span>Input</span><br>
                            <span>输入数据</span>
                            <br><span>---&gt;</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-green-200 text-center" @click="Show('demuxer')">
                            <span>demuxer</span><br>
                            <span>解复用</span>
                            <br><span>---&gt;</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-yellow-200 text-center" @click="Show('PacketData')">
                            <span>PacketData</span><br>
                            <span>数据包</span>
                            <br><span>---&gt;</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-green-200 text-center" @click="Show('decoder')">
                            <span>decoder</span><br>
                            <span>解码数据</span>
                            <br><span>---&gt;</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-yellow-200 text-center" @click="Show('FramesStream')">
                            <span>FramesStream</span><br>
                            <span>帧数据流</span>
                        </div>
                    </div>
                    <div class="flex flex-row justify-center my-5 text-center ">
                        <div class="p-2 ml-1 bg-blue-200 w-40">
                            <p>视频(Video)</p>
                            <p>音频(Video)</p>
                            <p>字幕(Subtitle)</p>
                        </div>
                        <div class="p-2 ml-1 bg-blue-200 w-40">数据的读写</div>
                        <div class="p-2 ml-1 bg-blue-200 w-40">
                            <p>复合文件结构</p>
                            <p>容器</p>
                        </div>
                        <div class="p-2 ml-1 bg-blue-200 w-40 align-middle">
                            <p>视频,音频,字幕</p>
                            <p>分解与组合</p>
                        </div>
                        <div class="p-2 ml-1 bg-blue-200 w-40">
                            <p>编码数据</p>
                        </div>
                        <div class="p-2 ml-1 bg-blue-200 w-40">
                            <p>视频,音频,字幕</p>
                            <p>编码与解码</p>
                        </div>
                        <div class="p-2 ml-1 w-40  h-full bg-green-200 text-center" @click="Show('filters')">
                            <span>↓</span><br>
                            <span>filters</span><br>
                            <span>帧处理(滤镜)</span>
                            <br><span>↓</span>
                        </div>
                    </div>
                    <div class="flex flex-row justify-center">
                        <div class="p-2 mr-1 w-40 h-full bg-yellow-200 text-center" @click="Show('Sink')">
                            <span>Sink</span><br>
                            <span>数据目标</span>
                        </div>
                        <div class="p-2 mr-1 w-40 h-full bg-green-200 text-center" @click="Show('protocols-write')">
                            <span>&lt;---</span><br>
                            <span>protocols-write</span><br>
                            <span>写入数据</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-yellow-200 text-center" @click="Show('Output')">
                            <span>&lt;---</span><br>
                            <span>Output</span><br>
                            <span>输出数据</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-green-200 text-center" @click="Show('muxer')">
                            <span>&lt;---</span><br>
                            <span>muxer</span><br>
                            <span>合复用</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-yellow-200 text-center" @click="Show('PacketData')">
                            <span>&lt;---</span><br>
                            <span>PacketData</span><br>
                            <span>数据包</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-green-200 text-center" @click="Show('encoder')">
                            <span>&lt;---</span><br>
                            <span>encoder</span><br>
                            <span>编码数据</span>
                        </div>
                        <div class="p-2 ml-1 w-40 h-full bg-yellow-200 text-center" @click="Show('FramesStream')">
                            <span>FramesStream</span><br>
                            <span>帧数据流</span>
                        </div>
                    </div>

                    <!-- 说明内容 -->
                    <div class="mt-1" v-if="showKey">
                        <div class="flex mt-2">
                            <div class="p-1 border bg-purple-200 mr-3">{{showKey}}</div>
                            <div class="p-1 border" :class="{'bg-red-200':showConKey == item}"
                                v-for="(item,k) in showCon" :key="k" @click="setShowCon(item)">{{item}}</div>
                        </div>
                        <div class="h-96 border mt-2 pl-5 py-3 overflow-y-scroll">
                            <pre> {{showConText}}</pre>
                        </div>
                    </div>
                </div>
            </div>
            <div class="mt-2 options-con" v-if="keyword || type == 'options'">
                <div class="mb-2 " v-for="(item,k) in options" :key="k">
                    <div class="p-1 text-blue-500 bg-green-200" @click="setOpionKey(item.id)">{{item.id}}</div>
                    <div class="" v-if="optionKey == item.id">
                        <div class="flex flex-row pl-5 bg-gray-200  mb-1 " v-for="(flag,fk) in item.flags" :key="fk">
                            <div class="px-2 w-2/5 text-blue-400">{{conType}} {{flag[0]}}</div>
                            <div class="px-2" :title="flag[2]">{{flag[1]}}</div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="mt-2 detail-con" v-if="keyword || type == 'detail'">
                <div class=" mb-1" v-for="(item,k) in details" :key="k" v-if="item.flags.length > 0">
                    <div class="p-1 text-blue-500 bg-green-200" @click="setDetailKey(item.id)">
                        {{item.id}}</div>
                    <div v-if="detailKey == item.id">
                        <div class="pl-5" v-for="(flag,fk) in item.flags" :key="fk">
                            <div class="flex flex-row  bg-gray-200  mb-1">
                                <div class="px-2 w-2/5 text-blue-400">{{conType}} {{flag.p}}</div>
                                <div class="w-1/6" :class="{'underline':flag.e.length > 0}" @click="setFlagKey(flag.p)">
                                    {{flag.t}}</div>
                                <div class="px-2 w-2/5" :title="flag.z">{{flag.d}}</div>
                            </div>
                            <div v-if="flagKey == flag.p">
                                <div class="flex flex-row pl-5  bg-gray-300  mb-1 " v-for="(e,ek) in flag.e" :key="ek">
                                    <div class="px-2 w-2/5 text-blue-400">{{conType}} {{flag.p}} {{e.v}}</div>
                                    <div class="w-1/6"></div>
                                    <div class="px-2 w-2/5" :title="e.z">{{e.d}}</div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    </div>

</body>
<script>
    let appOption = {
        el: "#app",
        data: {
            keyword: "",
            conType: "ffmpeg",//ffmpeg,ffprobe,ffplay
            type: "help", //help,options,detail
            optionKey: "",
            detailKey: "",
            flagKey: "",


            showKey: "",
            showCon: [],
            showConKey: "",
            showConText: "",

            help: {},
            options: [],
            details: []
        },
        created() {
            this.initData();
        },
        methods: {
            initData() {
                dataApi.getHelp(this.conType, this.keyword, d => {
                    this.help = d;
                });
                dataApi.getOptions(this.conType, this.keyword, d => {
                    this.options = d;
                })
                dataApi.getDetails(this.conType, this.keyword, d => {
                    this.details = d;
                })
            },
            setConType(type) {
                this.conType = type;
                this.initData();
            },
            setOpionKey(key) {
                this.optionKey = this.optionKey == key
                    ? ""
                    : key;
            },
            setDetailKey(key) {
                this.detailKey = this.detailKey == key
                    ? ""
                    : key;
            },
            setFlagKey(key) {
                this.flagKey = this.flagKey == key
                    ? ""
                    : key;
            },
            search() {
                this.initData();
            },
            Show(type) {
                this.showKey = this.showKey == type
                    ? ""
                    : type;
                if (this.showKey) {
                    let showMap = {
                        "source": "ffmpeg-devices.txt",
                        "protocols-read": "ffmpeg-protocols.txt",
                        "Input": "ffmpeg-formats.txt",
                        "demuxer": "ffmpeg-demuxers.txt",
                        "PacketData": "",
                        "decoder": "ffmpeg-decoders.txt",
                        "FramesStream": "ffmpeg-bsfs.txt,ffmpeg-colors.txt,ffmpeg-pix_fmts.txt,ffmpeg-layouts.txt",
                        "filters": "ffmpeg-filters.txt",
                        "encoder": "ffmpeg-encoders.txt",
                        "muxer": "ffmpeg-muxers.txt",
                        "Output": "ffmpeg-formats.txt",
                        "protocols-write": "ffmpeg-protocols.txt",
                        "Sink": "ffmpeg-devices.txt",
                    }
                    this.showCon = showMap[type].split(",");
                    if (this.showCon.length == 0) {
                        this.showKey = "";
                        return;
                    }
                    this.setShowCon(this.showCon[0]);
                }
            },
            setShowCon(key) {
                console.log("d", key);
                this.showConKey = key;
                axios.get(`./data/${this.conType}/${this.showConKey}`).then(res => {
                    this.showConText = res.data;
                })
            }
        },
    }
    let dataApi = {
        data: {
            keyword: "",
            help: {},
            options: [],
            details: [],
        },
        getHelp(conType, keyword, cb) {
            this.data.keyword = keyword;
            axios.get(`./json/${conType}.json`).then(res => {
                this.data.help = res.data;
                cb(this.helps());
            })
        },
        getOptions(conType, keyword, cb) {
            this.data.keyword = keyword;
            axios.get(`./json/${conType}/option.json`).then(res => {
                this.data.options = res.data;
                cb(this.options());
            })
        },
        getDetails(conType, keyword, cb) {
            this.data.keyword = keyword;
            axios.get(`./json/${conType}/detail.json`).then(res => {
                this.data.details = res.data;
                cb(this.details());
            })
        },
        helps() {
            keyword = this.data.keyword;
            if (keyword) {
                let _thelps = this.data.help.help.filter(
                    d => {
                        return d[0].includes(keyword)
                    }
                );
                this.data.help.help = _thelps;
            }
            return this.data.help;
        },
        options() {
            keyword = this.data.keyword;
            if (keyword) {
                return this.data.options.map(option => {
                    let _tflags = option.flags.filter(f => {
                        return f[0].includes(keyword);
                    });
                    if (_tflags.length > 0) {
                        option.flags = _tflags;
                        return option;
                    } else {
                        return null;
                    }
                }).filter(d => d);
            }
            return this.data.options;
        },
        details() {
            keyword = this.data.keyword;
            if (keyword) {
                return this.data.details.map(option => {
                    let _tflags = option.flags.filter(f => {
                        return f.p.includes(keyword);
                    });
                    if (_tflags.length > 0) {
                        option.flags = _tflags;
                        return option;
                    } else {
                        return null;
                    }
                }).filter(d => d);

            }
            return this.data.details;
        }
    }
    new Vue(appOption);

    function l(...msg) {
        console.log(msg);
    }
</script>

</html>